{# Sources into a collection, in steps (dialog body): its name (a new one only), the parts of this app to read (the twin knows each part's files, so nobody types a path; every part ticked to start), the file types to read of them (the loader's own ticked), files uploaded alongside, and a review of all of it with how much it is. Each step's answer shows beside it once passed. With ``name`` it adds to that collection. What it posts is what the API takes (``part``, ``extension``, ``file``). #} {% from "components/macros/layout.html" import dialog_title %} {% from "components/macros/form.html" import action, checkbox, field, file_button, form_footer, text_input, wizard, wizard_step %} {# Every one of ``ids`` (an Alpine expression) in ``key`` ticked, or none once all are: a group's one button, saying which it will do. #} {% macro all_or_none(label, key, ids, marker) %}{{ action(label, marker ~ " x-text=\"has('" ~ key ~ "', " ~ ids ~ ") ? 'None' : 'All'\" @click=\"flip('" ~ key ~ "', " ~ ids ~ ")\"") }}{% endmacro %} {# Each kind of part's ids, then every file type: what the All/None buttons flip. #} {% set groups = knowable.parts|map(attribute=1)|map("map", attribute=0)|map("list")|list %} {% set every_type = knowable.types|map(attribute=1)|sum(start=[]) %} {% set first = 0 if name else 1 %} {% set steps = ([] if name else [("Name", "title")]) + [("Source code", "partsSaid"), ("File types", "typesSaid"), ("Files", "filesSaid"), ("Review", None)] %} {{ dialog_title("Add to " ~ name if name else "New knowledge base", "What it holds: parts of this app, files of your own, or both.") }}
{% if name %}{% endif %} {% call wizard(steps) %} {% if not name %} {% call wizard_step(0) %} {% call field("Name", hint=named) %}{{ text_input("collection", None, "policies, code, handbook", required=True, autofocus=True, pattern=name_pattern, attrs='x-model="title"') }}{% endcall %} {% endcall %} {% endif %} {% call wizard_step(first) %}

This app's own code, {{ knowable.app }}, read from {{ knowable.root }}.

Each box is a part of the app: a service, a component, its shared code, or its docs. Everything is ticked; untick what you do not want read.

{% for group, picks in knowable.parts %}

{{ group }}

{{ all_or_none("None", "parts", "groups[" ~ loop.index0 ~ "]", "data-all-parts") }}
{% for id, part, what in picks %}
{{ checkbox("part", True, part, id, 'x-model="parts"') }}
{% endfor %}
{% endfor %} {% endcall %} {% call wizard_step(first + 1) %}

Which kinds of file to read from those parts. The number is how many files of that kind they hold.

{# Every type, or none once all are ticked: as a checklist's top button. #} {{ all_or_none("All", "exts", "types", "data-all-types") }}
{% for group, exts in knowable.types %}
{{ group }} {% for ext in exts %}{{ checkbox("extension", ext in knowable.ticked, ext, ext, 'x-model="exts"') }}{% endfor %}
{% endfor %} {% endcall %} {% call wizard_step(first + 2) %}

Any number, added alongside the source code: policies, handbooks, notes, anything that is not this app. Pick again to add more.

{{ file_button("ai-sources-files", "Choose files", knowable.readable|join(","), submit=False, required=False, multiple=True) }} {% endcall %} {% call wizard_step(first + 3) %}
{% if not name %}
Name
{% endif %}
Source code
of {{ knowable.app }}, from {{ knowable.root }}
File types
Files

{% if slow %}

Embedding runs on this server's CPU, one batch at a time, so a big pick takes a while. A worker (aegis add worker) spreads it out; a hosted model (RAG_EMBEDDING_PROVIDER=openai) is faster still.

{% endif %} {% endcall %} {{ form_footer("Add" if name else "Make knowledge base", wizard=True, attrs=':disabled="!total[0]"') }} {% endcall %}